<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>The source code</title>
  <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
  <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
  <style type="text/css">
    .highlight { display: block; background-color: #ddd; }
  </style>
  <script type="text/javascript">
    function highlight() {
      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
    }
  </script>
</head>
<body onload="prettyPrint(); highlight();">
  <pre class="prettyprint lang-js"><span id='global-property-S-'>/**
</span> * @fileOverview 表单元素
 * @ignore
 */


var $ = require(&#39;jquery&#39;),
  BUI = require(&#39;bui-common&#39;),
  Component = BUI.Component,
  TipItem = require(&#39;../tips&#39;).Item,
  Valid = require(&#39;../valid&#39;),
  Remote = require(&#39;../remote&#39;),
  CLS_FIELD_ERROR = BUI.prefix + &#39;form-field-error&#39;,
  CLS_TIP_CONTAINER = &#39;bui-form-tip-container&#39;,
  DATA_ERROR = &#39;data-error&#39;;

<span id='BUI-Form-FieldView'>/**
</span> * 字段视图类
 * @class BUI.Form.FieldView
 * @private
 */
var fieldView = Component.View.extend([Remote.View,Valid.View],{
  //渲染DOM
  renderUI : function(){
    var _self = this,
      control = _self.get(&#39;control&#39;);

    if(!control){
      var controlTpl = _self.get(&#39;controlTpl&#39;),
        container = _self.getControlContainer();
        
      if(controlTpl){
        var control = $(controlTpl).appendTo(container);
        _self.set(&#39;control&#39;,control);
      }
    }else{
      //var controlContainer = control.parent();
      _self.set(&#39;controlContainer&#39;,control.parent());
    }
  },
<span id='BUI-Form-FieldView-method-clearErrors'>  /**
</span>   * 清理显示的错误信息
   * @protected
   */
  clearErrors : function(){
    var _self = this,
      msgEl = _self.get(&#39;msgEl&#39;);
    if(msgEl){
      msgEl.remove();
      _self.set(&#39;msgEl&#39;,null);
    }
    _self.get(&#39;el&#39;).removeClass(CLS_FIELD_ERROR);
  },
<span id='BUI-Form-FieldView-method-showError'>  /**
</span>   * 显示错误信息
   * @param {String} msg 错误信息
   * @protected
   */
  showError : function(msg,errorTpl){
    var _self = this,
      control = _self.get(&#39;control&#39;),
      errorMsg = BUI.substitute(errorTpl,{error : msg}),
      el = $(errorMsg);
    //_self.clearErrorMsg();
    
    el.appendTo(control.parent());
    _self.set(&#39;msgEl&#39;,el);
    _self.get(&#39;el&#39;).addClass(CLS_FIELD_ERROR);
  },
<span id='BUI-Form-FieldView-method-getControlContainer'>  /**
</span>   * @internal 获取控件的容器
   * @return {jQuery} 控件容器
   */
  getControlContainer : function(){
    var _self = this,
      el = _self.get(&#39;el&#39;),
      controlContainer = _self.get(&#39;controlContainer&#39;);
    if(controlContainer){
      if(BUI.isString(controlContainer)){
        controlContainer = el.find(controlContainer);
      }
    }
    return (controlContainer &amp;&amp; controlContainer.length) ? controlContainer : el;
  },
<span id='BUI-Form-FieldView-method-getLoadingContainer'>  /**
</span>   * 获取显示加载状态的容器
   * @protected
   * @override
   * @return {jQuery} 加载状态的容器
   */
  getLoadingContainer : function () {
    return this.getControlContainer();
  },
  //设置名称
  _uiSetName : function(v){
    var _self = this;
    _self.get(&#39;control&#39;).attr(&#39;name&#39;,v);
  }
},
{
  ATTRS : {
    error:{},
    controlContainer : {},
    msgEl: {},
    control : {}
  }
});

<span id='BUI-Form-Field'>/**
</span> * 表单字段基类
 * @class BUI.Form.Field
 * @mixins BUI.Form.Remote
 * @mixins BUI.Form.Valid
 * @extends BUI.Component.Controller
 */
var field = Component.Controller.extend([Remote,Valid],{
  isField : true,
  initializer : function(){
    var _self = this;
    _self.on(&#39;afterRenderUI&#39;,function(){
      var tip = _self.get(&#39;tip&#39;);
      if(tip){
        var trigger = _self.getTipTigger();
        trigger &amp;&amp; trigger.parent().addClass(CLS_TIP_CONTAINER);
        tip.trigger = trigger;
        tip.autoRender = true;
        tip = new TipItem(tip);
        _self.set(&#39;tip&#39;,tip);
      }
    });
  },
  //绑定事件
  bindUI : function(){
    var _self = this,
      validEvent = _self.get(&#39;validEvent&#39;),
      changeEvent = _self.get(&#39;changeEvent&#39;),
      firstValidEvent = _self.get(&#39;firstValidEvent&#39;),
      innerControl = _self.getInnerControl();

    //选择框只使用 select事件
    if(innerControl.is(&#39;select&#39;)){
      validEvent = &#39;change&#39;;
    }
    //验证事件
    innerControl.on(validEvent,function(){
      var value = _self.getControlValue(innerControl);
      _self.validControl(value);
    });
    if(firstValidEvent){
      //未发生验证时，首次获取焦点/丢失焦点/点击，进行验证
      innerControl.on(firstValidEvent,function(){
        if(!_self.get(&#39;hasValid&#39;)){
          var value = _self.getControlValue(innerControl);
          _self.validControl(value);
        }
      });
    }
    

    //本来是监听控件的change事件，但是，如果控件还未触发change,但是通过get(&#39;value&#39;)来取值，则会出现错误，
    //所以当通过验证时，即触发改变事件
    _self.on(changeEvent,function(){
      _self.onValid();
    });

    _self.on(&#39;remotecomplete&#39;,function (ev) {
      _self._setError(ev.error);
    });

  },
<span id='BUI-Form-Field-method-onValid'>  /**
</span>   * 验证成功后执行的操作
   * @protected
   */
  onValid : function(){
    var _self = this,
      value =  _self.getControlValue();

    value = _self.parseValue(value);
    if(!_self.isCurrentValue(value)){
      _self.setInternal(&#39;value&#39;,value);
      _self.onChange();
    }
  },
  onChange : function () {
    this.fire(&#39;change&#39;);
  },
<span id='BUI-Form-Field-method-isCurrentValue'>  /**
</span>   * @protected
   * 是否当前值，主要用于日期等特殊值的比较，不能用 == 进行比较
   * @param  {*}  value 进行比较的值
   * @return {Boolean}  是否当前值
   */
  isCurrentValue : function (value) {
    return value == this.get(&#39;value&#39;);
  },
  //清理错误信息
  _clearError : function(){
    this.set(&#39;error&#39;,null);
    this.get(&#39;view&#39;).clearErrors();
  },
  //设置错误信息
  _setError : function(msg){
    this.set(&#39;error&#39;,msg);
    this.showErrors();
  },

<span id='BUI-Form-Field-method-getControlValue'>  /**
</span>   * 获取内部表单元素的值
   * @protected
   * @param  {jQuery} [innerControl] 内部表单元素
   * @return {String|Boolean} 表单元素的值,checkbox，radio的返回值为 true,false
   */
  getControlValue : function(innerControl){
    var _self = this;
    innerControl = innerControl || _self.getInnerControl();
    return innerControl.val();
  },
<span id='BUI-Form-Field-method-getControlContainer'>  /**
</span>   * @protected
   * 获取内部控件的容器
   */
  getControlContainer : function(){
    return this.get(&#39;view&#39;).getControlContainer();
  },
<span id='BUI-Form-Field-method-getRemoteParams'>  /**
</span>   * 获取异步验证的参数，对于表单字段域而言，是{[name] : [value]}
   * @protected
   * @override
   * @return {Object} 参数键值对
   */
  getRemoteParams : function  () {
    var _self = this,
      rst = {};
    rst[_self.get(&#39;name&#39;)] = _self.getControlValue();
    return rst;
  },
<span id='BUI-Form-Field-method-setControlValue'>  /**
</span>   * 设置字段的值
   * @protected
   * @param {*} value 字段值
   */
  setControlValue : function(value){
    var _self = this,
      innerControl = _self.getInnerControl();
    innerControl.val(value);
  },
<span id='BUI-Form-Field-method-parseValue'>  /**
</span>   * 将字符串等格式转换成
   * @protected
   * @param  {String} value 原始数据
   * @return {*}  该字段指定的类型
   */
  parseValue : function(value){
    return value;
  },
  valid : function(){
    var _self = this;
    _self.validControl();
  },
<span id='BUI-Form-Field-method-validControl'>  /**
</span>   * 验证控件内容
   * @return {Boolean} 是否通过验证
   */
  validControl : function(value){
    var _self = this, 
      errorMsg;
      value = value || _self.getControlValue(),
      preError = _self.get(&#39;error&#39;);
    errorMsg = _self.getValidError(value);
    _self.setInternal(&#39;hasValid&#39;,true);
    if (errorMsg) {
        _self._setError(errorMsg);
        _self.fire(&#39;error&#39;, {msg:errorMsg, value:value});
        if(preError !== errorMsg){//验证错误信息改变，说明验证改变
          _self.fire(&#39;validchange&#39;,{ valid : false });
        }
    } else {
        _self._clearError();
        _self.fire(&#39;valid&#39;);
        if(preError){//如果以前存在错误，那么验证结果改变
          _self.fire(&#39;validchange&#39;,{ valid : true });
        }
    }
    
    return !errorMsg;
  },
<span id='BUI-Form-Field-method-focus'>  /**
</span>   * 字段获得焦点
   */
  focus : function(){
    this.getInnerControl().focus();
  },
<span id='BUI-Form-Field-method-change'>  /**
</span>   * 字段发生改变
   */
  change : function(){
    var control = this.getInnerControl();
    control.change();
  },
<span id='BUI-Form-Field-method-blur'>  /**
</span>   * 字段丢失焦点
   */
  blur : function(){
    this.getInnerControl().blur();
  },

<span id='BUI-Form-Field-method-isValid'>  /**
</span>   * 是否通过验证,如果未发生过校验，则进行校验，否则不进行校验，直接根据已校验的结果判断。
   * @return {Boolean} 是否通过验证
   */
  isValid : function(){
    var _self = this;
    if(!_self.get(&#39;hasValid&#39;)){
      _self.validControl();
    }
    return !_self.get(&#39;error&#39;);
  },
<span id='BUI-Form-Field-method-getError'>  /**
</span>   * 获取验证出错信息
   * @return {String} 出错信息
   */
  getError : function(){
    return this.get(&#39;error&#39;);
  },
<span id='BUI-Form-Field-method-getErrors'>  /**
</span>   * 获取验证出错信息集合
   * @return {Array} 出错信息集合
   */
  getErrors : function(){
    var error = this.getError();
    if(error){
      return [error];
    }
    return [];
  },
<span id='BUI-Form-Field-method-clearErrors'>  /**
</span>   * 清理出错信息，回滚到未出错状态
   * @param {Boolean} reset 清除错误时，是否回滚上次正确的值
   */
  clearErrors : function(reset){
    var _self = this;
    _self._clearError();
    if(reset &amp;&amp; _self.getControlValue()!= _self.get(&#39;value&#39;)){
      _self.setControlValue(_self.get(&#39;value&#39;));
    }
  },
<span id='BUI-Form-Field-method-getInnerControl'>  /**
</span>   * 获取内部的表单元素或者内部控件
   * @protected
   * @return {jQuery|BUI.Component.Controller} 
   */
  getInnerControl : function(){
    return this.get(&#39;view&#39;).get(&#39;control&#39;);
  },
<span id='BUI-Form-Field-method-getTipTigger'>  /**
</span>   * 提示信息按照此元素对齐
   * @protected
   * @return {HTMLElement}
   */
  getTipTigger : function(){
    return this.getInnerControl();
  },
  //析构函数
  destructor : function(){
    var _self = this,
      tip = _self.get(&#39;tip&#39;);
    if(tip &amp;&amp; tip.destroy){
      tip.destroy();
    }
  },
<span id='BUI-Form-Field-method-setInnerWidth'>  /**
</span>   * @protected
   * 设置内部元素宽度
   */
  setInnerWidth : function(width){
    var _self = this,
      innerControl = _self.getInnerControl(),
      siblings = innerControl.siblings(),
      appendWidth = innerControl.outerWidth() - innerControl.width();

    BUI.each(siblings,function(dom){
      appendWidth += $(dom).outerWidth();
    });
    
    innerControl.width(width - appendWidth);
  },
  //重置 提示信息是否可见
  _resetTip :function(){
    var _self = this,
      tip = _self.get(&#39;tip&#39;);
    if(tip){
      tip.resetVisible();
    }
  },
<span id='BUI-Form-Field-method-resetTip'>  /**
</span>   * 重置显示提示信息
   * field.resetTip();
   */
  resetTip : function(){
    this._resetTip();
  },
  //设置值
  _uiSetValue : function(v){
    var _self = this;
    //v = v ? v.toString() : &#39;&#39;;
    _self.setControlValue(v);
    if(_self.get(&#39;rendered&#39;)){
      _self.validControl();
      _self.onChange();
    } 
    _self._resetTip();
  },
  //禁用控件
  _uiSetDisabled : function(v){
    var _self = this,
      innerControl = _self.getInnerControl(),
      children = _self.get(&#39;children&#39;);
    innerControl.attr(&#39;disabled&#39;,v);
    if(_self.get(&#39;rendered&#39;)){
      if(v){//控件不可用，清除错误
        _self.clearErrors();
      }
      if(!v){//控件可用，执行重新验证
        _self.valid();
      }
    }

    BUI.each(children,function(child){
      child.set(&#39;disabled&#39;,v);
    });

  },
  _uiSetWidth : function(v){
    var _self = this;
    if(v != null &amp;&amp; _self.get(&#39;forceFit&#39;)){
      _self.setInnerWidth(v);
    }
  }
},{
  ATTRS : {
<span id='BUI-Form-Field-property-hasValid'>    /**
</span>     * 是否发生过校验，初始值为空时，未进行赋值，不进行校验
     * @type {Boolean}
     */
    hasValid : {
      value : false
    },
<span id='BUI-Form-Field-property-forceFit'>    /**
</span>     * 内部元素是否根据控件宽度调整宽度
     * @type {Boolean}
     */
    forceFit : {
      value : false
    },
<span id='BUI-Form-Field-property-tip'>    /**
</span>     * 是否显示提示信息
     * @type {Object}
     */
    tip : {

    },
<span id='BUI-Form-Field-property-changeEvent'>    /**
</span>     * 表单元素或者控件内容改变的事件
     * @type {String}
     */
    changeEvent : {
      value : &#39;valid&#39;
    },
<span id='BUI-Form-Field-property-firstValidEvent'>    /**
</span>     * 未发生验证时，首次获取/丢失焦点，进行验证
     */
    firstValidEvent : {
      value : &#39;blur&#39;
    },
<span id='BUI-Form-Field-property-validEvent'>    /**
</span>     * 表单元素或者控件触发此事件时，触发验证
     * @type {String}
     */
    validEvent : {
      value : &#39;keyup change&#39;
    },
<span id='BUI-Form-Field-property-name'>    /**
</span>     * 字段的name值
     * @type {Object}
     */
    name : {
      view :true
    },
<span id='BUI-Form-Field-property-showError'>    /**
</span>     * 是否显示错误
     * @type {Boolean}
     */
    showError : {
      view : true,
      value : true
    },
<span id='BUI-Form-Field-cfg-value'>    /**
</span>     * 字段的值,类型根据字段类型决定
     * @cfg {*} value
     */
    value : {
      view : true
    },
<span id='BUI-Form-Field-property-label'>    /**
</span>     * 标题
     * @type {String}
     */
    label : {

    },
<span id='BUI-Form-Field-property-controlContainer'>    /**
</span>     * 控件容器，如果为空直接添加在控件容器上
     * @type {String|HTMLElement}
     */
    controlContainer : {
      view : true
    },
<span id='BUI-Form-Field-property-control'>    /**
</span>     * 内部表单元素的控件
     * @protected
     * @type {jQuery}
     */
    control : {
      view : true
    },
<span id='BUI-Form-Field-property-controlTpl'>    /**
</span>     * 内部表单元素的容器
     * @type {String}
     */
    controlTpl : {
      view : true,
      value : &#39;&lt;input type=&quot;text&quot;/&gt;&#39;
    },
    events: {
      value : {
<span id='BUI-Form-Field-event-error'>        /**
</span>         * 未通过验证
         * @event
         */
        error : false,
<span id='BUI-Form-Field-event-valid'>        /**
</span>         * 通过验证
         * @event
         */
        valid : false,
<span id='BUI-Form-Field-event-change'>        /**
</span>         * @event
         * 值改变，仅当通过验证时触发
         */
        change : true,

<span id='BUI-Form-Field-event-validchange'>        /**
</span>         * @event
         * 验证改变
         * @param {Object} e 事件对象
         * @param {Object} e.target 触发事件的对象
         * @param {Boolean} e.valid 是否通过验证
         */
        validchange : true
      }  
    },
    tpl: {
      value : &#39;&lt;label&gt;{label}&lt;/label&gt;&#39;
    },
    xview : {
      value : fieldView 
    }
  },
  PARSER : {
    control : function(el){
      var control = el.find(&#39;input,select,textarea&#39;);
      if(control.length){
        return control;
      }
      return el;
    },
    disabled : function(el){
      return !!el.attr(&#39;disabled&#39;);
    },
    value : function(el){
      var _self = this,
        selector = &#39;select,input,textarea&#39;,
        value = _self.get(&#39;value&#39;);
      if(!value){
        if(el.is(selector)){
          value = el.val();
          if(!value &amp;&amp; el.is(&#39;select&#39;)){
            value = el.attr(&#39;value&#39;);
          }
        }else{
          value = el.find(selector).val(); 
        }
        
      }
      return  value;
    },
    name : function(el){
      var _self = this,
        selector = &#39;select,input,textarea&#39;,
        name = _self.get(&#39;name&#39;);
      if(!name){
        if(el.is(selector)){
          name = el.attr(&#39;name&#39;);
        }else{
          name = el.find(selector).attr(&#39;name&#39;); 
        }
        
      }
      return  name;
    }
    
  }
},{
  xclass:&#39;form-field&#39;
});

field.View = fieldView;

module.exports = field;
</pre>
</body>
</html>
